#include"PageCache.h"

//PageCache* PageCache::_owner=nullptr;

PageCache PageCache::_owner;

// 一次获取k页
span* PageCache::NewSpan(size_t k)
{
	if (k > NPAGES)
	{
		void* ptr = SystemAlloc(k<<PAGE_SHIFT);

		// 加入映射
		span* newSpan = _spanPool.New();
		newSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		newSpan->_length = k; 
		newSpan->_objSize = k << PAGE_SHIFT;
		newSpan->_isUse = true;

		// 此处大内存不必全部页映射，不会交给Central回收
		_idToSpan.set(newSpan->_pageId, newSpan);
		return newSpan;
	}



	//恰好有对应页，直接返回对应页下的span
	if (!_spanLists[k].Empty())
	{
		span* retSpan = _spanLists[k].PopFront();

		// 全部插入，方便central回收内存时找到span
		for (int i = 0; i < retSpan->_length; i++)
		{
			//_idToSpan[retSpan->_pageId + i] = retSpan;
			_idToSpan.set(retSpan->_pageId + i, retSpan);
		}

		retSpan->_isUse = true;
		return retSpan;
	}

	// 向后找大块内存
	for (int i = k + 1; i <= NPAGES; i++)
	{
		if (!_spanLists[i].Empty())
		{
			span* bigSpan = _spanLists[i].PopFront();

			span* retSpan = _spanPool.New();// 返回的k页Span

			retSpan->_pageId = bigSpan->_pageId;
			retSpan->_length = k;

			bigSpan->_pageId = bigSpan->_pageId + k;
			bigSpan->_length -= k;

			// 头尾加入hash中方便合并span
			//_idToSpan[bigSpan->_pageId] = bigSpan; //修改之后没有错误了？
			//_idToSpan[bigSpan->_pageId + bigSpan->_length - 1] = bigSpan;
			_idToSpan.set(bigSpan->_pageId, bigSpan);
			_idToSpan.set(bigSpan->_pageId + bigSpan->_length - 1, bigSpan);
			
			// 不断地申请释放，如果不修改成上述的话，旧的信息可能代替新的在合并的时候采用了
			//  加入清理函数就不会报错，但合并不了
			//_idToSpan[retSpan->_pageId] = retSpan;  
			//_idToSpan[retSpan->_pageId + retSpan->_length - 1] = retSpan;

			// 全部插入，方便central回收内存时找到span 与Page cache合并span
			for (int i = 0; i < retSpan->_length; i++)
			{
				//_idToSpan[retSpan->_pageId + i] = retSpan;
				_idToSpan.set(retSpan->_pageId + i, retSpan);
			}

			_spanLists[bigSpan->_length].PushFront(bigSpan); // 切分后的插入Span表

			retSpan->_isUse = true;// 已经在使用，不可被合并

			return retSpan;
		}
	}

	// 后序没有大块内存
	void* start=SystemAlloc(NPAGES);

	span* newSpan = _spanPool.New();// 返回的k页Span
	newSpan->_pageId = (PAGE_ID)start >> PAGE_SHIFT;
	newSpan->_length = NPAGES;
	
	_spanLists[NPAGES].PushFront(newSpan);

	//递归调用，复用代码
	return NewSpan(k);
}




void PageCache::ReleaseSpanToPageCache(span* span1)
{
	// 1-128页PageCache管理即[8k,1024k], 256-1024k也可以管理
	//129页之上，直接释放
	if (span1->_length> NPAGES)
	{
		SystemFree((void*)(span1->_pageId << PAGE_SHIFT));
		//delete span1;
		_spanPool.Delete(span1);
		return;
	}

	// 向前合并
	span* curSpan = span1;
	while (1)
	{
		int prevId = curSpan->_pageId - 1;
		//auto it = _idToSpan.find(prevId);
		span* it=(span*)_idToSpan.get(prevId);

		// 没找到前页
		//if (it == _idToSpan.end())
		//{
		//	break;
		//}
		if (it == nullptr)
		{
			break;
		}
		// 正在被使用
		span* prevSpan = it;
		if (prevSpan->_isUse == true)
		{
			break;
		}
		// 合并超过最大页数
		if (prevSpan->_length + curSpan->_length > NPAGES)
		{
			break;
		}

		// 合并两页
		curSpan->_pageId = prevSpan->_pageId;
		curSpan->_length += prevSpan->_length;

		_spanLists[prevSpan->_length].Erase(prevSpan);// 删除合并前页
		//removeSpecificSpans(prevSpan);

		_spanPool.Delete(prevSpan);
		//delete prevSpan;
	}


	//向后合并
	while (1)
	{
		int nextId = curSpan->_pageId +curSpan->_length;
		//auto it = _idToSpan.find(nextId);
		span* it = (span*)_idToSpan.get(nextId);

		// 没找到后页
		if (it == nullptr)
		{
			break;
		}

		// 正在被使用
		span* nextSpan = it;
		if (nextSpan->_isUse == true)
		{
			break;
		}

		// 合并超过最大页数
		if (nextSpan->_length + curSpan->_length > NPAGES)
		{
			break;
		}

		//合并两页
		curSpan->_length += nextSpan->_length;

		_spanLists[nextSpan->_length].Erase(nextSpan);// 删除合并前页
		//removeSpecificSpans(nextSpan);  用来测试旧信息_idToSpan影响
		//delete nextSpan;
		_spanPool.Delete(nextSpan);
	}


	// 插入合并后或者没有合并的span
	_spanLists[curSpan->_length].PushFront(curSpan);
	curSpan->_isUse = false;

	// 更新页与span映射关系
	//_idToSpan[curSpan->_pageId + curSpan->_length - 1] = curSpan;
	//_idToSpan[curSpan->_pageId] = curSpan;
	_idToSpan.set(curSpan->_pageId, curSpan);
	_idToSpan.set(curSpan->_pageId + curSpan->_length - 1, curSpan);

}



span* PageCache::MapObjectToSpan(void* obj)
{
	//_pageMtx.lock();   // 一个线程读取，一个修改会造成错误

	size_t id = (PAGE_ID)obj >> PAGE_SHIFT;
	span* it = (span*)_idToSpan.get(id);
	assert(it);
	return it;

	//auto ret = _idToSpan.find(id);
	//if (ret != _idToSpan.end())
	//{
	//	_pageMtx.unlock();
	//	return ret->second;
	//}
	//else
	//{
	//	_pageMtx.unlock();
	//	assert(false);
	//	return nullptr;
	//}
	
}